package tech.mhuang.pacebox.springboot.core.spring.start;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import tech.mhuang.pacebox.core.exception.BusinessException;
import tech.mhuang.pacebox.core.util.CollectionUtil;

import java.util.Map;

/**
 * spring启动装载类
 *
 * @author mhuang
 * @since 1.0.0
 */
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    private static DefaultListableBeanFactory beanFactory;

    /**
     * 获取上下文
     *
     * @return ApplicationContext 上下文
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取bean工厂接口
     *
     * @return bean工厂接口
     * @since 2021.0.5.0
     */
    public static BeanFactory getBeanFactory() {
        return beanFactory;
    }

    /**
     * 上下文初始化
     *
     * @param applicationContext 传递的上下文
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        if (SpringContextHolder.applicationContext == null) {
            SpringContextHolder.applicationContext = applicationContext;
            beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        }
    }

    /**
     * name是否包含bean
     *
     * @param name name
     * @return boolean
     * @since 2021.0.0.4
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**
     * 通过名字获取上下文中的bean
     *
     * @param name bean方法名
     * @return Object 返回对应bean
     */
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    /**
     * 通过名字获取上下文中的bean
     *
     * @param name  bean方法名
     * @param clazz 获取Bean指定的class
     * @param <T>   获取Bean指定的Class类型
     * @return BeanName的Bean
     */

    public static <T> T getBean(String name, Class<T> clazz) {
        return applicationContext.getBean(name, clazz);
    }

    /**
     * 通过名字删除上下文的bean
     *
     * @param name 删除的BeanName
     */
    public static void removeBean(String name) {
        beanFactory.removeBeanDefinition(name);
    }

    /**
     * bean注册
     *
     * @param beanName 注册的Bean Name
     * @param clazz    注册的Bean class
     * @param <T>      注册的Bean Class类型
     * @return 返回注册的Bean
     */
    public static <T> T registerBean(String beanName, Class<T> clazz) {
        return registerBean(beanName, clazz, null);
    }

    /**
     * bean注册
     *
     * @param beanName 注册的Bean Name
     * @param clazz    注册的Bean class
     * @param <T>      注册的Bean Class类型
     * @param params   注册的Bean 传递的参数
     * @param args     注册的bean 类参数
     * @return 返回注册的Bean
     */
    public static <T> T registerBean(String beanName, Class<T> clazz, Map<String, Object> params, Object... args) {
        if (applicationContext.containsBean(beanName)) {
            Object bean = applicationContext.getBean(beanName);
            if (bean.getClass().isAssignableFrom(clazz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName 重复 " + beanName);
            }
        }
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        if (CollectionUtil.isNotEmpty(params)) {
            params.forEach(beanDefinitionBuilder::addPropertyValue);
        }
        if (CollectionUtil.isNotEmpty(args)) {
            for (Object arg : args) {
                beanDefinitionBuilder.addConstructorArgValue(arg);
            }
        }
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
        beanFactory.registerBeanDefinition(beanName, beanDefinition);
        return getBean(beanName, clazz);
    }

    /**
     * 根据class获取上下文的bean
     *
     * @param requiredType 获取Bean的Class
     * @param <T>          获取的class类型
     * @return Object 返回对应Bean
     */
    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

    /**
     * 根据type获取bean相关配置
     *
     * @param type class
     * @param <T>  class类型
     * @return 所有集合
     * @since 2020.0.2.2
     */
    public static <T> Map<String, T> getBeansOfType(Class<T> type) {
        return applicationContext.getBeansOfType(type);
    }

    /**
     * 获取属性
     *
     * @param key 属性key
     * @return 属性值
     * @since 2021.0.5.0
     */
    public static String getProperty(String key) {
        return getProperty(key, String.class);
    }

    /**
     * 获取属性
     *
     * @param key          属性的key
     * @param defaultValue 没有属性的时候默认值
     * @return 结果
     * @since 2021.0.5.0
     */
    public static String getProperty(String key, String defaultValue) {
        return getProperty(key, String.class, defaultValue);
    }

    /**
     * 获取属性
     *
     * @param key   属性key
     * @param clazz 属性得到的对象
     * @param <T>   属性类型
     * @return 属性
     * @since 2021.0.5.0
     */
    public static <T> T getProperty(String key, Class<T> clazz) {
        return getProperty(key, clazz, null);
    }

    /**
     * 获取属性
     *
     * @param key          属性key
     * @param clazz        属性得到的对象
     * @param defaultValue 属性默认值
     * @param <T>          属性类型
     * @return 属性
     */
    public static <T> T getProperty(String key, Class<T> clazz, T defaultValue) {
        return getBean(Environment.class).getProperty(key, clazz, defaultValue);
    }

    /**
     * 获取应用名称
     *
     * @return 应用名称
     * @since 2021.0.5.1
     */
    public static String getApplicationName() {
        return applicationContext.getApplicationName();
    }

    /**
     * 移除bean
     *
     * @param beanName bean名称
     * @since 2021.0.5.1
     */
    public static void unregisterBean(String beanName) {
        final ConfigurableListableBeanFactory factory = (DefaultListableBeanFactory) getBeanFactory();
        if (factory instanceof DefaultSingletonBeanRegistry registry) {
            registry.destroySingleton(beanName);
        } else {
            throw new BusinessException("Can not unregister bean, the factory is not a DefaultSingletonBeanRegistry!");
        }
    }

    /**
     * 发布事件
     *
     * @param event 事件
     * @since 2021.0.5.1
     */
    public static void publishEvent(ApplicationEvent event) {
        applicationContext.publishEvent(event);
    }

    /**
     * 发布事件
     *
     * @param event 事件
     * @since 2021.0.5.1
     */
    public static void publishEvent(Object event) {
        applicationContext.publishEvent(event);
    }
}